#pragma once

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// 定义一个函数 MyDaemon，用于将普通进程转换为守护进程
void MyDaemon()
{
    // 1. 忽略特定信号：SIGPIPE 和 SIGCHLD
    //   SIGPIPE：当进程尝试写入到已断开连接的管道时触发，守护进程通常忽略此信号，避免因意外断开的网络连接而终止。
    //   SIGCHLD：当子进程终止或停止时发送给其父进程，守护进程通常忽略此信号，以避免僵尸进程积累，并让子进程自动清理。
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

    // 2. 使用 fork 创建子进程，并使父进程退出，使子进程成为孤儿进程，由 init 进程接管
    //   这样做是为了确保守护进程不是进程组的组长，与原会话和控制终端彻底分离。
    if (fork() > 0)
        exit(0);

    // 3. 调用 setsid 创建新的会话并成为该会话的组长，同时与原控制终端脱离关联
    //   守护进程不再受任何终端的控制，真正成为后台进程。
    setsid();

    // 4. 将标准输入（stdin）、标准输出（stdout）和标准错误（stderr）重定向至 /dev/null
    //   /dev/null 是一个特殊的设备文件，所有写入的数据都将被丢弃，读取则永远返回空。
    //   这样做是为了防止守护进程尝试向终端输出信息（可能导致错误或阻塞），以及避免不必要的输入操作。
    int devnull = open("/dev/null", O_RDONLY | O_WRONLY);
    if (devnull > 0)
    {
        // 使用 dup2 将标准输入、输出、错误的文件描述符替换为指向 /dev/null 的文件描述符
        dup2(0, devnull);  // stdin
        dup2(1, devnull);  // stdout
        dup2(2, devnull);  // stderr

        // 关闭原始的 /dev/null 文件描述符，保留重定向后的副本
        close(devnull);
    }
}